#####################################################################
#
# MSPrint SDK FOR PYTHON
# AUTHOR: zengkai001@qq.com
# DATE: 2025-04-11
#
#####################################################################

import ctypes
import os
from pathlib import Path
from typing import Optional, Union

class MSPrintSDK:
    def __init__(self, lib_path: Optional[str] = None):
        """
        Initialize the MSPrint SDK
        
        Args:
            lib_path: Optional path to the libmsprintsdk.so library. If not provided,
                     will look in PYTHON_SDK/x86_64/ directory.
        """
        if lib_path is None:
            # Get the directory where this file is located
            current_dir = Path(__file__).parent
            lib_path = str(current_dir / "x86_64" / "libmsprintsdk.so")
            
        if not os.path.exists(lib_path):
            raise FileNotFoundError(f"Library not found at {lib_path}")
            
        self.lib = ctypes.CDLL(lib_path)
        
        # Initialize function prototypes
        self._setup_function_prototypes()
        
    def _setup_function_prototypes(self):
        """Setup ctypes function prototypes for all SDK functions"""
        # Device setup functions
        self.lib.SetDevname.argtypes = [ctypes.c_int, ctypes.c_char_p, ctypes.c_int]
        self.lib.SetDevname.restype = ctypes.c_int
        
        self.lib.SetPrintermodel.argtypes = [ctypes.c_int]
        self.lib.SetPrintermodel.restype = ctypes.c_int
        
        self.lib.SetInit.argtypes = []
        self.lib.SetInit.restype = ctypes.c_int
        
        self.lib.SetClean.argtypes = []
        self.lib.SetClean.restype = ctypes.c_int
        
        self.lib.SetClose.argtypes = []
        self.lib.SetClose.restype = ctypes.c_int
        
        # Codepage functions
        self.lib.SetCodepage.argtypes = [ctypes.c_int, ctypes.c_int]
        self.lib.SetCodepage.restype = ctypes.c_int
        
        # Text formatting functions
        try:
            self.lib.SetCharspace.argtypes = [ctypes.c_int]
            self.lib.SetCharspace.restype = ctypes.c_int
        except AttributeError:
            self.lib.SetCharspace = None
            
        self.lib.SetLinespace.argtypes = [ctypes.c_int]
        self.lib.SetLinespace.restype = ctypes.c_int
        
        self.lib.SetAlignment.argtypes = [ctypes.c_int]
        self.lib.SetAlignment.restype = ctypes.c_int
        
        self.lib.SetFont.argtypes = [ctypes.c_int]
        self.lib.SetFont.restype = ctypes.c_int
        
        self.lib.SetBold.argtypes = [ctypes.c_int]
        self.lib.SetBold.restype = ctypes.c_int
        
        self.lib.SetLeftmargin.argtypes = [ctypes.c_int]
        self.lib.SetLeftmargin.restype = ctypes.c_int
        
        # Printing functions
        self.lib.PrintString.argtypes = [ctypes.c_char_p, ctypes.c_int]
        self.lib.PrintString.restype = ctypes.c_int
        
        self.lib.PrintFeedline.argtypes = [ctypes.c_int]
        self.lib.PrintFeedline.restype = ctypes.c_int
        
        self.lib.PrintCutpaper.argtypes = [ctypes.c_int]
        self.lib.PrintCutpaper.restype = ctypes.c_int
        
        self.lib.PrintQrcode.argtypes = [ctypes.c_char_p, ctypes.c_int, ctypes.c_int, ctypes.c_int]
        self.lib.PrintQrcode.restype = ctypes.c_int
        
        # Status functions
        self.lib.GetStatus.argtypes = []
        self.lib.GetStatus.restype = ctypes.c_int
        
        # Chinese character support
        self.lib.SetReadZKmode.argtypes = [ctypes.c_int]
        self.lib.SetReadZKmode.restype = ctypes.c_int
        
    def set_device(self, dev_type: int, dev_name: str, dev_value: int) -> int:
        """
        Set the printer device name and type
        
        Args:
            dev_type: Device type
            dev_name: Device name/path
            dev_value: Device value
            
        Returns:
            int: Result code
        """
        return self.lib.SetDevname(dev_type, dev_name.encode(), dev_value)
        
    def set_printer_model(self, model: int) -> int:
        """
        Set the printer model
        
        Args:
            model: Printer model number
            
        Returns:
            int: Result code
        """
        return self.lib.SetPrintermodel(model)
        
    def initialize(self) -> int:
        """
        Initialize the printer
        
        Returns:
            int: Result code
        """
        return self.lib.SetInit()
        
    def cleanup(self) -> int:
        """
        Clean up printer resources
        
        Returns:
            int: Result code
        """
        return self.lib.SetClean()
        
    def close(self) -> int:
        """
        Close the printer connection
        
        Returns:
            int: Result code
        """
        return self.lib.SetClose()
        
    def set_char_space(self, space: int) -> int:
        """
        Set character spacing
        
        Args:
            space: Space value
            
        Returns:
            int: Result code (0 if function not available)
        """
        if hasattr(self.lib, 'SetCharspace') and self.lib.SetCharspace:
            return self.lib.SetCharspace(space)
        return 0  # Return success if function not available
        
    def set_line_space(self, space: int) -> int:
        """
        Set line spacing
        
        Args:
            space: Space value
            
        Returns:
            int: Result code
        """
        return self.lib.SetLinespace(space)
        
    def set_alignment(self, alignment: int) -> int:
        """
        Set text alignment
        
        Args:
            alignment: Alignment value (0=left, 1=center, 2=right)
            
        Returns:
            int: Result code
        """
        return self.lib.SetAlignment(alignment)
        
    def set_font(self, font: int) -> int:
        """
        Set font type
        
        Args:
            font: Font number
            
        Returns:
            int: Result code
        """
        return self.lib.SetFont(font)
        
    def set_bold(self, bold: int) -> int:
        """
        Set bold mode
        
        Args:
            bold: 0=normal, 1=bold
            
        Returns:
            int: Result code
        """
        return self.lib.SetBold(bold)
        
    def set_left_margin(self, margin: int) -> int:
        """
        Set left margin
        
        Args:
            margin: Margin value in dots
            
        Returns:
            int: Result code
        """
        return self.lib.SetLeftmargin(margin)
        
    def set_codepage(self, country: int, cp_number: int) -> int:
        """
        Set printer codepage (character encoding)
        
        Args:
            country: Country code
            cp_number: Code page number
            
        Returns:
            int: Result code
        """
        return self.lib.SetCodepage(country, cp_number)

    def print_text(self, text: str, immediate: bool = False) -> int:
        """
        Print text
        
        Args:
            text: Text to print
            immediate: Whether to print immediately
            
        Returns:
            int: Result code
        """
        # 强制使用 GBK 编码，如果有无法转换的字符将使用?替代
        text_bytes = text.encode('gbk', errors='replace')
        return self.lib.PrintString(text_bytes, 1 if immediate else 0)
        
    def feed_lines(self, lines: int) -> int:
        """
        Feed paper by specified number of lines
        
        Args:
            lines: Number of lines to feed
            
        Returns:
            int: Result code
        """
        return self.lib.PrintFeedline(lines)
        
    def cut_paper(self, mode: int = 1) -> int:
        """
        Cut paper
        
        Args:
            mode: Cut mode (0=partial cut, 1=full cut)
            
        Returns:
            int: Result code
        """
        return self.lib.PrintCutpaper(mode)
        
    def print_qrcode(self, data: str, left_margin: int = 0, module_size: int = 4, error_correction: int = 0) -> int:
        """
        Print QR code
        
        Args:
            data: QR code data
            left_margin: Left margin in dots
            module_size: Module size
            error_correction: Error correction level
            
        Returns:
            int: Result code
        """
        return self.lib.PrintQrcode(data.encode(), left_margin, module_size, error_correction)
        
    def get_status(self) -> int:
        """
        Get printer status
        
        Returns:
            int: Status code
        """
        return self.lib.GetStatus()
        
    def set_read_zk_mode(self, mode: int = 0) -> int:
        """
        Set ZK mode for Chinese character support
        
        Args:
            mode: ZK mode (0=enable)
            
        Returns:
            int: Result code
        """
        return self.lib.SetReadZKmode(mode)
        
    def __enter__(self):
        """Context manager entry"""
        return self
        
    def __exit__(self, exc_type, exc_val, exc_tb):
        """Context manager exit"""
        self.cleanup()
        self.close()
